/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkSGNode_DEFINED
#define SkSGNode_DEFINED

#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"

#include <vector>

class SkCanvas;
class SkMatrix;

namespace sksg {
class InvalidationController;

/* *
 * Base class for all scene graph nodes.
 *
 * Handles ingress edge management for the DAG (i.e. node -> "parent" node mapping),
 * and invalidation.
 *
 * Note: egress edges are only implemented/supported in container subclasses
 * (e.g. Group, Effect, Draw).
 */
class Node : public SkRefCnt {
public:
    // Traverse the DAG and revalidate any dependant/invalidated nodes.
    // Returns the bounding box for the DAG fragment.
    const SkRect &revalidate(InvalidationController *, const SkMatrix &);

    // Tag this node for invalidation and optional damage.
    void invalidate(bool damage = true);

protected:
    enum InvalTraits {
        // Nodes with this trait never generate direct damage -- instead,
        // the damage bubbles up to ancestors.
        kBubbleDamage_Trait = 1 << 0,

        // Nodes with this trait obscure the descendants' damage and always override it.
        kOverrideDamage_Trait = 1 << 1,
    };

    explicit Node(uint32_t invalTraits);
    ~Node() override;

    const SkRect &bounds() const
    {
        SkASSERT(!this->hasInval());
        return fBounds;
    }

    bool hasInval() const
    {
        return fFlags & kInvalidated_Flag;
    }

    // Dispatched on revalidation.  Subclasses are expected to recompute/cache their properties
    // and return their bounding box in local coordinates.
    virtual SkRect onRevalidate(InvalidationController *, const SkMatrix &ctm) = 0;

    // Register/unregister |this| to receive invalidation events from a descendant.
    void observeInval(const sk_sp<Node> &);
    void unobserveInval(const sk_sp<Node> &);

private:
    enum Flags {
        kInvalidated_Flag = 1 << 0,   // the node or its descendants require revalidation
        kDamage_Flag = 1 << 1,        // the node contributes damage during revalidation
        kObserverArray_Flag = 1 << 2, // the node has more than one inval observer
        kInTraversal_Flag = 1 << 3,   // the node is part of a traversal (cycle detection)
    };

    template <typename Func> void forEachInvalObserver(Func &&) const;

    class ScopedFlag;

    union {
        Node *fInvalObserver;
        std::vector<Node *> *fInvalObserverArray;
    };
    SkRect fBounds;
    const uint32_t fInvalTraits : 2;
    uint32_t fFlags : 4;     // Internal flags.
    uint32_t fNodeFlags : 8; // Accessible from select subclasses.
    // Free bits                         : 18;

    friend class NodePriv;
    friend class RenderNode; // node flags access

    using INHERITED = SkRefCnt;
};

// Helper for defining attribute getters/setters in subclasses.
#define SG_ATTRIBUTE(attr_name, attr_type, attr_container) \
    const attr_type &get##attr_name() const                \
    {                                                      \
        return attr_container;                             \
    }                                                      \
    void set##attr_name(const attr_type &v)                \
    {                                                      \
        if (attr_container == v)                           \
            return;                                        \
        attr_container = v;                                \
        this->invalidate();                                \
    }                                                      \
    void set##attr_name(attr_type &&v)                     \
    {                                                      \
        if (attr_container == v)                           \
            return;                                        \
        attr_container = std::move(v);                     \
        this->invalidate();                                \
    }

#define SG_MAPPED_ATTRIBUTE(attr_name, attr_type, attr_container) \
    attr_type get##attr_name() const                              \
    {                                                             \
        return attr_container.get##attr_name();                   \
    }                                                             \
    void set##attr_name(const attr_type &v)                       \
    {                                                             \
        if (attr_container.get##attr_name() == v)                 \
            return;                                               \
        attr_container.set##attr_name(v);                         \
        this->invalidate();                                       \
    }                                                             \
    void set##attr_name(attr_type &&v)                            \
    {                                                             \
        if (attr_container.get##attr_name() == v)                 \
            return;                                               \
        attr_container.set##attr_name(std::move(v));              \
        this->invalidate();                                       \
    }
} // namespace sksg

#endif // SkSGNode_DEFINED
